package com.cafe.pro.aop;

import com.cafe.pro.exception.CryptoConfigurationException;
import com.cafe.pro.exception.CryptoException;
import com.cafe.pro.manager.KeyManager;
import com.cafe.pro.util.CryptoUtils;
import jakarta.annotation.PostConstruct;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Arrays;

@Aspect
@Component
@Slf4j
@AllArgsConstructor
public class EncryptionAspect {
    private final KeyManager keyManager;

    @PostConstruct
    public void show() {
        System.out.println("EncryptionAspect 初始化成功！"); // 观察控制台输出
    }

    // 加密切面：拦截保存操作
    @Around("execution(* com.cafe.pro.service.UserService.*(..))")
    public Object encrypt(ProceedingJoinPoint joinPoint) throws Throwable {
        Object entity = joinPoint.getArgs()[0];
        processEncrypt(entity);
        return joinPoint.proceed(new Object[]{entity});
    }

    // 实体加密处理
    private void processEncrypt(Object entity) {
        Class<?> clazz = entity.getClass();
        Arrays.stream(clazz.getDeclaredFields())
                .filter(f -> f.isAnnotationPresent(EncryptedField.class))
                .forEach(field -> processFieldEncryption(entity, field));
    }

    private void processFieldEncryption(Object entity, Field plainField) {
        try {
            EncryptedField annotation = plainField.getAnnotation(EncryptedField.class);
            String encryptedFieldName = annotation.value();

            // 获取加密字段
            Field encryptedField = getDeclaredField(entity.getClass(), encryptedFieldName);

            // 获取原始值
            plainField.setAccessible(true);
            Object plainValue = plainField.get(entity);
            if (plainValue == null) return;

            // AES加密
            String currentAesKey = keyManager.getAesKey();
            String encryptedValue = CryptoUtils.encryptWithAes(plainValue.toString(), currentAesKey);

            // 设置加密值
            encryptedField.setAccessible(true);
            encryptedField.set(entity, encryptedValue);

            // 设置密钥版本
            Field versionField = getDeclaredField(entity.getClass(), "keyVersion");
            if (versionField != null) {
                versionField.setAccessible(true);
                versionField.set(entity, keyManager.getCurrentKeyVersion());
            }

        } catch (Exception e) {
            log.error("字段加密失败: {}#{}", entity.getClass().getSimpleName(), plainField.getName(), e);
            throw new CryptoException("字段加密失败", e);
        }
    }

    // 安全获取字段（带校验）
    private Field getDeclaredField(Class<?> clazz, String fieldName) {
        try {
            return clazz.getDeclaredField(fieldName);
        } catch (NoSuchFieldException e) {
            String errorMsg = String.format("字段配置错误: 类[%s]中未找到字段[%s]", clazz.getSimpleName(), fieldName);
            log.error(errorMsg);
            return null;
        }
    }
}
